package com.mongodb.util;


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

public class CustomSnowflakeIdUtil {

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final int SEQUENCE_BITS = 5; // 序列号占用的位数
    private static final int MAX_SEQUENCE = -1 ^ (-1 << SEQUENCE_BITS); // 序列号的最大值
    private static final long TIME_STAMP_BITS = 14; // 时间戳占用的位数

    private static final long SEQUENCE_SHIFT = 0; // 序列号左移位数
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS; // 时间戳左移位数

    private static long lastTimestamp = -1L; // 上一次生成ID的时间戳
    private static AtomicInteger sequence = new AtomicInteger(0); // 序列号

    public static synchronized long nextId() {
        long timestamp = getCurrentTimestamp();

        // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        // 如果是同一时间生成的，则进行毫秒内序列
        if (lastTimestamp == timestamp) {
            sequence.getAndIncrement();
            // 毫秒内序列溢出
            if (sequence.get() > MAX_SEQUENCE) {
                // 阻塞到下一个毫秒,获得新的时间戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 时间戳改变，毫秒内序列重置
            sequence.set(0);
        }

        // 上次生成ID的时间戳
        lastTimestamp = timestamp;

        // 移位并通过或运算拼到一起组成64位的ID
        long id = ((timestamp << TIMESTAMP_SHIFT) | (sequence.get() << SEQUENCE_SHIFT));

        return id;
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 当前时间戳
     */
    protected static long tilNextMillis(long lastTimestamp) {
        long timestamp = getCurrentTimestamp();
        while (timestamp <= lastTimestamp) {
            timestamp = getCurrentTimestamp();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒为单位的当前时间
     * @return 当前时间(毫秒)
     */
    protected static long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }

    public static synchronized String generateCustomId() {
        long id = nextId();
        Long timestampPart = nextId();
        String sequencePart = String.format("%05d", id & MAX_SEQUENCE);
        return timestampPart + sequencePart;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(generateCustomId());
        }
    }
}
